// <copyright file="DungeonGenerator.UniqueProperties.cs" company="TenneySoftware">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Robert Tenney</author>
// <email>rob10e@yahoo.com</email>
// <date>April 2009</date>
// <summary>Dungeon level generator.</summary>

// License:

// Product: RPGRoguelikeEngine .Net
// Author: Robert N. Tenney
// Copyright: April 2009

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation version 2
// of the License.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// This license covers all source code, binaries, and other materials related to this project
// unless specifically stated otherwise.

using System;
using System.Collections.Generic;
using TenneySoftware.Engine;

namespace TenneySoftware.LevelGenerator.DungeonGenerator {
    /// <content>
    /// This file contains the Unqie (Not defined within ILevelGenerator interface) properties for the generator.
    /// </content>
    public partial class Dungeon {
        /// <summary>
        /// Gets or sets a value between 0 - 100 that represents the density of of the level. The higher the number, the
        /// more dense it will be. A value of 100 equals 100 percent density.
        /// </summary>
        /// <value>
        /// The density of the level.
        /// </value>
        public int Density {
            get {
                return this.density;
            }

            set {
                if (value < 0 || value > 100) {
                    throw new ValueOutsideOfRangeException("Value is not between 0 - 100. (" + value.ToString(System.Globalization.CultureInfo.CurrentCulture) + ")");
                }

                this.density = value;
                this.SetName("Custom");
            }
        }

        /// <summary>
        /// Gets or sets a value between 1 - 100 that the generator will use to determine if a structure to be built is not using
        /// a preferred connector, should it build from a room, or from a hallway. The higher the number, the more likely it is
        /// to build from a room rather than a hall. Default is 50.
        /// </summary>
        /// <value>
        /// The RoomBias value.
        /// </value>
        public int RoomBias {
            get {
                return this.roomBias;
            }

            set {
                if (value > 100 || value < 0) {
                    throw new ValueOutsideOfRangeException("Value is not within range 0 - 100. (" +
                                                           value.ToString(System.Globalization.CultureInfo.CurrentCulture) + ")");
                } else {
                    this.roomBias = value;
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the generator should allow wandering halls to intersect other halls or rooms, or avoid
        /// contact with them.
        /// </summary>
        /// <value>
        /// Flag that indicates whether or not to allow hallways to intersect other hallways.
        /// </value>
        public bool IntersectWanderingHalls {
            get {
                return this.intersectWanderingHalls;
            }

            set {
                this.SetName("Custom");
                this.intersectWanderingHalls = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to perform a dead end removal sweep after generation.
        /// </summary>
        /// <value>
        /// Flag that indicates whether or not to remove dead ends from hallways.
        /// </value>
        public bool RemoveDeadEnds {
            get {
                return this.removeDeadEnds;
            }

            set {
                this.removeDeadEnds = value;
                this.SetName("Custom");
            }
        }

        /// <summary>
        /// Gets or sets a number between 0 and 100 to determine if the generator should use a prefered connector or select a
        /// random point. A value of 0 disables the prefered connectors and uses only random points. The higher the number, the
        /// more likely it will use a preferred connector. Default is 50.
        /// </summary>
        /// <value>
        /// The chance to use preferred connectors.
        /// </value>
        public int UsePreferredConnectors {
            get {
                return this.preferredConnectorsChance;
            }

            set {
                if (value > 100 || value < 0) {
                    throw new ValueOutsideOfRangeException("Value is not within range 0 - 100. (" +
                                                           value.ToString(System.Globalization.CultureInfo.CurrentCulture) + ")");
                } else {
                    this.preferredConnectorsChance = value;
                    this.SetName("Custom");
                }
            }
        }

        /// <summary>
        /// Gets the structure weights Dictionary List. The format for the Dictionary List is:
        /// Dictionary&lt;StructureType, double&gt;.
        /// </summary>
        /// <value>
        /// Dictionary of structure weights.
        /// </value>
        public Dictionary<StructureType, double> StructureWeightsList {
            get {
                return this.structureWeights;
            }
        }

        /// <summary>
        /// Gets the structure weights Dictionary List. The format for the Dictionary List is:
        /// Dictionary&lt;int, double&gt;.
        /// </summary>
        /// <value>
        /// Dictionary of structure weights integers.
        /// </value>
        public Dictionary<int, double> StructureWeightsListInt {
            get {
                Dictionary<int, double> temp = new Dictionary<int, double>();
                foreach (KeyValuePair<StructureType, double> kvp in this.structureWeights) {
                    temp.Add((int)kvp.Key, kvp.Value);
                }

                return temp;
            }
        }
        
        /// <summary>
        /// Sets the Structure Weights list.
        /// </summary>
        /// <param name="list">List to set the Structure Weights to.</param>
        public void SetStructureWeightsList(Dictionary<StructureType, double> list) {
            this.structureWeights = list;
        }
        
        /// <summary>
        /// Sets the Structure Weights list.
        /// </summary>
        /// <param name="list">List to set the Structure Weights to.</param>
        public void SetStructureWeightsList(Dictionary<int, double> list) {
            this.structureWeights = new Dictionary<StructureType, double>();
            foreach (KeyValuePair<int, double> kvp in list) {
                this.structureWeights.Add((StructureType)kvp.Key, kvp.Value);
            }
        }

        /// <summary>
        /// Retrieves a list of properties that are unique to this generator and not to any other.
        /// </summary>
        /// <returns>An array of GeneratorData objects with information about the unique properties within this generator.</returns>
        public GeneratorData[] GetUniquePropertyInformation() {
            GeneratorData[] result = new GeneratorData[6];
            result[0] = new GeneratorData(
                0,
                "IntersectWanderingHalls",
                "Sets the flag to indicate whether or not wandering halls can intersect other halls.",
                new string[] { typeof(bool).ToString() });
            result[1] = new GeneratorData(
                1,
                "RoomBias",
                "Sets the bias for selecting a room to generate.",
                new string[] { typeof(int).ToString() });
            result[2] = new GeneratorData(
                2,
                "UsePreferredConnectors",
                "Sets the chance to build new structures from the end of hallway.",
                new string[] { typeof(int).ToString() });
            result[3] = new GeneratorData(
                3,
                "RemoveDeadEnds",
                "Sets the flag to indicate whether or not to remove dead ends.",
                new string[] { typeof(bool).ToString() });
            result[4] = new GeneratorData(
                4,
                "Density",
                "Sets the initial density of the level.",
                new string[] { typeof(int).ToString() });
            result[5] = new GeneratorData(
                5,
                "StructureWeightsList",
                "Gets or sets the structure weights Dictionary List.",
                new string[] { typeof(Dictionary<int, double>).ToString() });
            return result;
        }

        /// <summary>
        /// Passes parameters to unique properties within this generator through the ILevelSettings interface. Properties
        /// available are:
        /// 0 - IntersectWanderingHalls - bool
        /// 1 - RoomBias - int
        /// 2 - UsePreferredConnectors - int
        /// 3 - RemoveDeadEnds - bool
        /// 4 - Density - int
        /// 5 - StructureWeightsList - Dictionary&lt;int, double&gt;
        /// options usage: new object[][]{new object[]{3, true}}
        /// To turn the RemoveDeadEnds flag to true.
        /// </summary>
        /// <param name="options">Jagged array of objects with the property code at the first position, and the value
        /// to pass in the second position.</param>
        public void SetUniqueProperties(object[][] options) {
            if (options.GetLength(0) > 6) {
                throw new ArgumentException("Too many options in array. Recheck your options.");
            }
            
            foreach (object[] args in options) {
                switch ((int)args[0]) {
                    case 0:
                        if (args[1] is bool) {
                            this.IntersectWanderingHalls = (bool)args[1];
                        } else {
                            throw new ArgumentException("Option needs to be a " + 
                                                        typeof(bool).ToString() + 
                                                        " type to set the flag to indicate whether or not wandering halls can intersect other halls.");
                        }
                        
                        break;
                    case 1:
                        if (args[1] is int) {
                            this.RoomBias = (int)args[1];
                        } else {
                            throw new ArgumentException("Option needs to be a " + 
                                                        typeof(int).ToString() + 
                                                        " type to set the bias for selecting a room to generate.");
                        }

                        break;
                    case 2:
                        if (args[1] is int) {
                            this.UsePreferredConnectors = (int)args[1];
                        } else {
                            throw new ArgumentException("Option needs to be a " + 
                                                        typeof(int).ToString() + 
                                                        " type to set the chance to build new structures from the end of hallways.");
                        }
                        
                        break;
                    case 3:
                        if (args[1] is bool) {
                            this.RemoveDeadEnds = (bool)args[1];
                        } else {
                            throw new ArgumentException("Option needs to be a " + 
                                                        typeof(bool).ToString() + 
                                                        " type to set the flag to indicate whether or not to remove dead ends.");
                        }
                        
                        break;
                    case 4:
                        if (args[1] is int) {
                            this.Density = (int)args[1];
                        } else {
                            throw new ArgumentException("Option needs to be a " + 
                                                        typeof(int).ToString() + 
                                                        " type to set the initial density of the level.");
                        }
                        
                        break;
                    case 5:
                        if (args[1] is Dictionary<int, double>) {
                            this.SetStructureWeightsList((Dictionary<int, double>)args[1]);
                        } else {
                            throw new ArgumentException("Option needs to be a " + 
                                                        typeof(Dictionary<int, double>).ToString() + 
                                                        " type to set the structure weight list of the level.");
                        }
                        
                        break;
                }
            }
        }
    }
}